package memnet.graph.builder;

import java.io.File;
import java.io.InputStream;
import java.util.Scanner;
import java.util.regex.MatchResult;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import memnet.graph.Graph;
import static jstlib.IO.*;
import static jstlib.Lang.*;

public class GraphParser {
	
	public static void main(String[] args) {
		File f = new File("res//graph.txt");
		Graph g = parseGraph(f, IGraphBuilder.basicGraphBuilder);
		System.out.println(g.debugInfo());
	}
	
	final static double VERSION = 1.0;
	
	final static Pattern comment = Pattern.compile("^\\s*(#.*)?$");
	final static Pattern version_comment = Pattern.compile("^\\s*#\\s*vers:([0-9.]+)\\s*$");
	final static Pattern line = Pattern.compile(strMult("([\\w.]*:[\\w.\"() ]*?)",4)+"(\\s*#.*)?$");
	final static Pattern element = Pattern.compile("([\\w.]*):([\\w.\"() ]*)");
	
	final static Pattern javalangstr = Pattern.compile("(java\\.lang\\.String||String)");
	
	public static Graph parseGraph(File f, IGraphBuilder gb){
		gb.init();
		InputStream i = openFileIn(f);
		Scanner s = new Scanner(i);
		int lineNum = 0;
		while( s.hasNextLine()){
			lineNum++;
			String nline = s.nextLine();
			
			if( lineNum==1){ //look for version number
				checkVersionNumber(nline);
			}
			
			parseLine(lineNum, nline, gb);
		}
		s.close();
		return gb.getGraph();
	}
	
	private static void parseLine(int lineNum, String s, IGraphBuilder gb){
		if( comment.matcher(s).find()){
			return;
		}
		//System.out.println(s);
		Matcher m = line.matcher(s);
		if( m.find()){
			MatchResult mr = m.toMatchResult();
			
			Object node1 = null;
			Object node2 = null;
			Object edge = null;
			Object context = null;
			
			for(int i=1; i<=4; i++){
				String elem= mr.group(i);
				
				Matcher ematch = element.matcher(elem);
				if( ematch.find()){
					String clazz = ematch.group(1).trim();
					String obj = ematch.group(2).trim();
					
					if( i==1 ){
						node1 =getNode(lineNum, clazz, obj);
					}else if (i==2){
						node2 =getNode(lineNum, clazz, obj);					
					}else if( i==3){
						edge =getEdge(lineNum, clazz, obj);
					}else if(i==4){
						context =getContext(lineNum, clazz,obj);
					}
					
				}else{
					throw new GraphParseError(lineNum, "Cannot parse line: "+s);
				}
			}
			
			gb.addEdge(node1, node2, edge, context);
			
			
		}else{
			throw new GraphParseError(lineNum, "Cannot parse line: "+s);		
		}
		
	}
	
	private static void checkVersionNumber(String line){
		Matcher m = version_comment.matcher(line);
		if( m.find()){
			String vers = m.group(1);
			try{
				double fileVersion = Double.parseDouble(vers);
				if( fileVersion > VERSION){
					System.err.printf("WARNING: Version of file (%s) exceeds version of parser (%s).\n", fileVersion, VERSION);
				}
				
			}catch( NumberFormatException e){
				System.err.println("WARNING: Invalid version "+vers);
			}
			//System.out.println(vers);
		}else{
			System.err.println("WARNING: Could not detect version. Make sure the version comment is the first line of the file (ex. #vers:1.0)");
		}
	}
	
	private static Object getNode(int lineNum, String clazz, String obj){
		if( clazz.equals("null")){
			return null;
		}
		if( !javalangstr.matcher(clazz).matches()){
			throw new GraphParseError(lineNum, "Invalid class "+clazz+"; parser only supports strings");
		}
		//System.out.println("N:"+clazz+","+obj);
		return obj;
	}
	
	private static Object getContext(int lineNum, String clazz, String obj){
		if( clazz.equals("null")){
			return null;
		}
		if( !javalangstr.matcher(clazz).matches()){
			throw new GraphParseError(lineNum, "Invalid class "+clazz+"; parser only supports strings");
		}
		//System.out.println("C:"+clazz+","+obj);	
		return obj;
	}
	
	private static Object getEdge(int lineNum, String clazz, String obj){
		if( clazz.equals("null")){
			return null;
		}
		if( !javalangstr.matcher(clazz).matches()){
			throw new GraphParseError(lineNum, "Invalid class "+clazz+"; parser only supports strings");
		}
		//System.out.println("E:"+clazz+","+obj);
		return obj;
	}

}
